/*
    nspire-gamekit - a simple homebrew game framework
    Copyright (C) 2011  Daniel Tang

    This program is free software: you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation, either version 3 of the License, or
    (at your option) any later version.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with this program.  If not, see <http://www.gnu.org/licenses/>.
*/
#include <os.h>
#include "autorelease.h"

/* An attempt at an autorelease pool */

typedef struct memnode_t memnode_t;

struct memnode_t {
    memnode_t *next;
    memnode_t *prev;
    unsigned int ref;
    void* ptr;
};

static inline void freeNode(memnode_t *node);

static memnode_t* firstElement = NULL;
static memnode_t* lastElement = NULL;
static int memAllocCount = 0;

void* gameAutoMalloc(size_t size) {
    memnode_t *node = malloc(sizeof(memnode_t));
    if (!node) return NULL;
    node->next = NULL;
    node->prev = NULL;
    node->ptr = malloc(size+sizeof(memnode_t*));
    node->ref = 1;
    
    *((memnode_t**)node->ptr) = node;
    
    if (!node->ptr) {
        free(node);
        return NULL;
    }
    
    if (firstElement == NULL) {
        firstElement = node;
    }
    if (lastElement) {
        lastElement->next = node;
        node->prev = lastElement;
    }
    lastElement = node;
    
    memAllocCount++;
    return (((char*)node->ptr)+sizeof(memnode_t*));
}

void* gameAutoRetain(void *ptr) {
    memnode_t *node = *(((memnode_t**)ptr)-1);
    node->ref++;
    return ptr;
}

void gameAutoRelease(void *ptr) {
    memnode_t *node = *(((memnode_t**)ptr)-1);
    node->ref--;
    if (node->ref == 0) {
        gameAutoFree(ptr);
    }
}


void gameAutoFree(void *ptr) {
    memnode_t *node = *(((memnode_t**)ptr)-1);
    if (node->ref != 0) {
        printf("Warning: Freeing memory with references to it\n");
    }
    
    free(node->ptr);
    
    memAllocCount--;
    freeNode(node);
}

static inline void freeNode(memnode_t *node) {
    /* Unlink from the double linked list */
    if (node->prev) node->prev->next = node->next;
    if (node->next) node->next->prev = node->prev;
    
    if (firstElement == node) firstElement = node->next;
    if (lastElement == node) lastElement = node->prev;
    
    free(node);
}
    
/* Cleans up all memory woo! */
void gameAutoDrain() {
    memnode_t *node = firstElement;
    memnode_t *next = NULL;
    
    while (node) { 
        next = node->next;
        free(node->ptr);
        free(node);
        node = next;
        memAllocCount--;
    }
    firstElement = NULL;
    lastElement = NULL;
}

int gameMemCount() {
    return memAllocCount;
}